package app.ring;

import java.io.Serializable;
import java.net.InetAddress;
import java.net.InetSocketAddress;

import org.commons.ByteFunctions;
import org.commons.HashFunction;



public class Node implements Serializable {

	
	private static final long serialVersionUID = -7488542619597778839L;
	private final String name;
	private final byte[] nameAsBytes;
	private InetSocketAddress address;
	// array di interi contenente gli hash dei nodi virtuali
	private Integer[] virtualNodesHashes;
	private final int hash;
	private final byte[] hashAsBytes;

	
	/**
	 *  Il costruttore Node() si occupa di inizializzare i seguenti parametri
	 *  
	 *  @param nodeName nome del nodo 
	 *  @param nodeAddress indirizzo relativo al nodo
	 *  @param virtualNodes numero di nodi virtuali relativi al nodo
	 *     
	 */
	public Node(String nodeName, InetSocketAddress nodeAddress, int virtualNodes) {
		this.name = nodeName;
		this.nameAsBytes = ByteFunctions.getBytes(this.name);
		this.address = nodeAddress;

		// Creazione degli hash che rappresenteranno i nodi virtuali relativi a ciascun nodo
		this.virtualNodesHashes = new Integer[virtualNodes];
		for(int i = 0; i < virtualNodes; i++) {
			this.virtualNodesHashes[i] = HashFunction.hash(address.getAddress().getHostAddress() + i);
		}

		this.hash = HashFunction.hash(name + address.toString()); // hash del nodo
		this.hashAsBytes = ByteFunctions.getBytes(this.hash);
	}

	/**
	 *  La funzione getName() ritorna il nome dell'oggetto
	 * @return String
	 */
	public String getName() {
		return name;
	}

	/**
	 *  La funzione getInetSocketAddress() restituisce l'InetSocketAddress (l'indirizzo IP + numero di porta) del nodo
	 *  
	 * @return InetSocketAddress indirizzo IP+  numero di porta
	 * 
	 */
	public InetSocketAddress getInetSocketAddress() {
		return address;
	}
	
	/**
	 *  La funzione setInetSocketAddress() permette di impostare l'InetSocketAddress (l'indirizzo IP + numero di porta) del nodo
	 *  
	 * @param address l'indirizzo IP + numero di porta del nodo
	 *
	 */
	public void setInetSocketAddress(InetSocketAddress address) {
		this.address = address;
	}
	
	/**
	 *  La funzione getAddress() restituisce l'InetAddress (l'indirizzo IP) del nodo
	 *  
	 * @return InetAddress indirizzo IP
	 *
	 */
	public InetAddress getAddress() {
		
		return address.getAddress();
	}
	
	/**
	 *  La funzione getPort() restituisce il numero di porta di ascolto
	 *  
	 * @return int 
	 *
	 */
	public int getPort() {
		return address.getPort();
	}

	/**
	 *  La funzione getVirtualNodesHash() restiutisce  un array contenente l'hash dei nodi virtuali
	 *  
	 * @return  Integer[] array di interi contenente l'hash dei nodi virtuali 
	 *
	 */
	public Integer[] getVirtualNodesHash() {
		return virtualNodesHashes;
	}

	/**
	 *  La funzione getNodeHash() restituisce l'hash del nodo
	 *  
	 * @return int hash del nodo 
	 *
	 */
	public int getNodeHash() {
		return hash;
	}
	
	/**
	 *  La funzione getNameAsBytes() restituisce un array di byte rappresentante il nome del nodo
	 *  
	 * @return byte[] 
	 *
	 */
	public byte[] getNameAsBytes() {
		return nameAsBytes;
	}

	/**
	 *  La funzione getVirtualNodesHashes() restituisce un array di interi rappresentante gli hash dei nodi virtuali
	 *  
	 * @return Integer[] 
	 *
	 */
	public Integer[] getVirtualNodesHashes() {
		return virtualNodesHashes;
	}
	
	/**
	 *  La funzione getHash() restituiscel'hash del nodo
	 *  
	 * @return int 
	 *
	 */
	public int getHash() {
		return hash;
	}
	
	/**
	 *  La funzione getHashAsBytes() restituisce un array di byte rappresentante l'hash del nodo
	 *  
	 * @return byte[]
	 *
	 */
	public byte[] getHashAsBytes() {
		return hashAsBytes;
	}

	@Override
	public boolean equals(Object o) {
		if(this.getClass().equals(o.getClass()))
			return equals((Node) o);
		else
			return this == o;
	}

	private boolean equals(Node n) {
		return hash == n.hash;
	}

	@Override
	public String toString() {
		return name.toString();
	}
}
